#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <cstdlib>
#include <pthread.h>
using namespace std;

const uint32_t gDefaultCap = 5;
template <class T>
class BlockQueue
{
public:
    BlockQueue(uint32_t cap = gDefaultCap) : cap_(cap)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&consumerCond_, nullptr);
        pthread_cond_init(&productorCond_, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&consumerCond_);
        pthread_cond_destroy(&productorCond_);
    }
    // 生产接口
    void push(const T &in) // const &:纯输入
    {
        lockQueue();
        while (isFull()) // isFull就是我们在临界区设定的条件
        {
            // befor:当等待的时候，会自动释放mutex_
            proBlockWait(); // 阻塞等待，等待被唤醒  被唤醒 ！= 条件被满足
            // after:当醒来的时候，是在临界区醒来的
        }
        // 条件满足，可以生产数据
        pushCore(in);
        unlockQueue();
        wakeupCon(); // 唤醒消费者
    }
    // 消费接口
    T pop() // type *:输出
    {
        lockQueue();
        while (isEmpty())
        {
            conBlockWait();
        }
        // 条件满足，可以消费数据
        T tmp = popCore();
        unlockQueue();
        wakeupPro(); // 唤醒生产者
        return tmp;
    }

private:
    void lockQueue()
    {
        pthread_mutex_lock(&mutex_);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&mutex_);
    }

    bool isEmpty()
    {
        return bq_.empty();
    }

    bool isFull()
    {
        return bq_.size() == cap_;
    }

    void proBlockWait() // 生产者一定是在临界区中的
    {
        // 1.在阻塞线程的时候，会自动释放metex_锁
        pthread_cond_wait(&productorCond_, &mutex_);
        // 2.当阻塞结束，返回的时候，pthread_cond_wait，会自动重新获得mutex_，然后才返回
    }

    void conBlockWait()
    {
        pthread_cond_wait(&consumerCond_, &mutex_);
    }

    void wakeupCon()
    {
        pthread_cond_signal(&consumerCond_);
    }

    void wakeupPro()
    {
        pthread_cond_signal(&productorCond_);
    }

    void pushCore(const T &in)
    {
        bq_.push(in);
    }

    T popCore()
    {
        T tmp = bq_.front();
        bq_.pop();
        return tmp;
    }

private:
    uint32_t cap_;                 // 容量
    queue<T> bq_;                  // blockqueue
    pthread_mutex_t mutex_;        // 保护阻塞队列的互斥锁
    pthread_cond_t consumerCond_;  // 让消费者等待的条件变量
    pthread_cond_t productorCond_; // 让生产者等待的条件变量
};